﻿#r "system.management.dll"

open System
open System.Management
open Microsoft.FSharp.Core

ManagementPath.DefaultPath.Server <- "vmshark10"
ManagementPath.DefaultPath.NamespacePath <- "root\\virtualization"
               

type WmiMethodInvoker(instance:ManagementObject) =
    member private x.instance = instance
    static member (?) (this:WmiMethodInvoker, methname:string): (obj array->'a) =
        fun args -> unbox (this.instance.InvokeMethod(methname,args))

type WmiAssociators(instance:ManagementObject) =
    member private x.instance = instance
    static member (?) (this:WmiAssociators, sel:string) =
        match sel.Split(':') with
        | [| resultcls; assoccls |] -> this.instance.GetRelated(resultcls) |> Seq.cast |> Seq.map(fun x->new WmiObj(x))
        | [| resultcls |] -> this.instance.GetRelated(resultcls) |> Seq.cast |> Seq.map(fun x->new WmiObj(x))
        | _ -> Seq.empty
and WmiObj (instance:ManagementObject) =
    member private x.instance = instance
    static member (?) (this:WmiObj, propname:string) =
        unbox (this.instance.[propname])
    static member (?<-) (this:WmiObj, propname:string, propval) =
        this.instance.[propname] <- propval
    member x.invoke = new WmiMethodInvoker(instance)
    member x.assoc = new WmiAssociators(instance)
    member x.text = x.instance.GetText(TextFormat.CimDtd20)
    member x.path = x.instance.Path.Path

type WmiEnum (prefix:string) =
    member private x.prefix = prefix
    static member (?) (this:WmiEnum, clsname:string) =
        let cls = new ManagementClass(this.prefix+clsname)
        cls.GetInstances() |> Seq.cast |> Seq.map(fun x->new WmiObj(x))

let msvm = new WmiEnum("msvm_")
let cim = new WmiEnum("cim_")

// -------------------------



let get_vm () : WmiObj seq =
    msvm?computersystem |> Seq.filter(fun x->x?Caption="Virtual Machine")

let new_globalsetting() =
    let instance = (new ManagementClass("Msvm_VirtualSystemGlobalSettingData")).CreateInstance()
    new WmiObj(instance)

let get_globalsetting (vm:WmiObj) =
    vm.assoc?Msvm_VirtualSystemGlobalSettingData
    |> Seq.head

let get_currentsetting (vm:WmiObj) =
    vm.assoc?``msvm_VirtualSystemSettingData:Msvm_SettingsDefineState``
    |> Seq.head

let get_cpu (setting:WmiObj) =
    setting.assoc?Msvm_ProcessorSettingData
    |> Seq.head

let get_ide (setting:WmiObj) =
    setting.assoc?Msvm_ResourceAllocationSettingData
    |> Seq.filter(fun x->x?ResourceSubType="Microsoft Emulated IDE Controller")

let get_all_res (setting:WmiObj) =
    setting.assoc?CIM_ResourceAllocationSettingData

////////////////////////
// JOB
////////////////////////

exception JobFailure of uint16 * string

type JobState =
    | New = 2
    | Starting = 3
    | Running = 4
    | Suspended = 5
    | ShuttingDown = 6
    | Completed = 7
    | Terminated = 8
    | Killed = 9
    | Exception = 10
    | Service = 11

let rec wait_for(jobpath:string) =
    if jobpath=null then
        let job = new WmiObj(new ManagementObject(jobpath))
        let jobstate = job?JobState
        if jobstate = (uint16 JobState.Running) || jobstate = (uint16 JobState.Starting) then
            System.Threading.Thread.Sleep(1000)
            wait_for(jobpath)
        if jobstate <> (uint16 JobState.Completed) then
            raise (JobFailure(job?ErrorCode, job?Description))
    ()


////////////////////////
// RETURN CODE
////////////////////////

exception APIFailure of uint32

type ReturnCode = 
    | Completed = 0
    | Started = 4096
    | Failed = 32768
    | AccessDenied = 32769
    | NotSupported = 32770
    | Unknown = 32771
    | Timeout = 32772
    | InvalidParameter = 32773
    | SystemInUse = 32774
    | InvalidState = 32775
    | IncorrectDataType = 32776
    | SystemNotAvailable = 32777
    | OutofMemory = 32778

let check_ret (ret:uint32) =
    if ret <> (uint32 ReturnCode.Completed) && ret <> (uint32 ReturnCode.Started) then
        raise (APIFailure(ret))
    ()

////////////////////////
// VMMS
////////////////////////


type Vmms() =
    member private x.vmms = msvm?VirtualSystemManagementService |> Seq.head
    member this.definevm (setting:string) =
        let args:obj array = [| setting; null; null; 0; 0 |]
        let ret:uint32 = this.vmms.invoke?DefineVirtualSystem args
        let vmpath:string = unbox args.[3]
        let jobpath:string = unbox args.[4]
        printfn "%A %A" vmpath jobpath
        wait_for jobpath
        check_ret ret
        vmpath
    member this.modifyres (vmpath:string) (res:string array) =
        let args:obj array = [| vmpath; res; 0 |]
        let ret:uint32 = this.vmms.invoke?ModifyVirtualSystemResources args
        let jobpath:string= unbox args.[2]
        printfn "%A" jobpath
        wait_for jobpath
        check_ret ret
        ()
    member this.destoryvm (vmpath:string) =
        let args:obj array = [| vmpath; 0 |]
        let ret:uint32 = this.vmms.invoke?DestroyVirtualSystem args
        let jobpath:string  = unbox args.[1]
        wait_for jobpath
        check_ret ret
        ()
    member this.addres (vmpath:string) (res:string array) =
        let args:obj array = [| vmpath; res; 0; 0 |]
        let ret:uint32 = this.vmms.invoke?AddVirtualSystemResources args
        let jobpath:string = unbox args.[3]
        wait_for jobpath
        check_ret ret
        ()

let t= new_globalsetting()

let vmms = new Vmms()
let vm1= get_vm()|>Seq.head
//vmms.definevm (t.text)

let vm1_cpu = vm1|>get_currentsetting|>get_cpu
vm1_cpu?VirtualQuantity <- 1

vmms.modifyres vm1.path [|vm1_cpu.text|]

let ``jifewjiwjefijjijij fjewifweifwj with fewjfiew `` = 1

